package eu.irreality.dai.gameplay.entities;

import java.awt.Color;
import java.util.LinkedList;
import java.util.List;

import eu.irreality.dai.gameplay.world.GameWorld;
import eu.irreality.dai.ui.cell.DisplayableObject;
import eu.irreality.dai.util.Position;

/**
 * Root class for the hierarchy of entities of the game. An entity is everything
 * that can be displayed.
 * 
 */
public abstract class Entity implements DisplayableObject
{

    protected char c;
    protected Color color;
    protected int displayPriority;

    private Position position;

    private List<PropertyEntry> properties = new LinkedList<PropertyEntry>();

    public Entity()
    {

    }

    public Entity(char c, Color color)
    {
	this.c = c;
	this.color = color;
    }

    @Override
    public char getChar()
    {
	return c;
    }

    @Override
    public Color getColor()
    {
	return color;
    }

    @Override
    public int getDisplayPriority()
    {
	return displayPriority;
    }

    public void update(GameWorld world)
    {
	for (PropertyEntry p : properties)
	{
	    p.decreaseTime();
	    if (p.needsUpdate())
	    {
		update(p, world);
	    }
	}
    }

    /**
     * This abstract method determines how a subclass of Entity reacts to the
     * need of updating a certain property (i.e. when a property's timer reaches
     * zero).
     * 
     * @param pe
     *            The property entry to be updated.
     * @param world
     *            The game world in which this entity is.
     */
    public abstract void update(PropertyEntry pe, GameWorld world);

    public Position getPosition()
    {
	return position;
    }

    public void setPosition(Position position)
    {
	this.position = position;
    }

    public void setPosition(int x, int y)
    {
	this.position = new Position(x, y);
    }

    /*
     * Convenience methods for querying and handling properties
     */

    public List<PropertyEntry> getProperties()
    {
	return properties;
    }

    public String getPropertyValue(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getValue();
	}
	return null;
    }

    public String getPropertyValueAsString(String propertyName)
    {
	return getPropertyValue(propertyName);
    }

    public Object getPropertyValueAsObject(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getValueAsObject();
	}
	return null;
    }

    public boolean getPropertyValueAsBoolean(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getValueAsBoolean();
	}
	return false;
    }

    public int getPropertyValueAsInteger(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getValueAsInteger();
	}
	return 0;
    }

    public double getPropertyValueAsDouble(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getValueAsDouble();
	}
	return 0;
    }

    public float getPropertyValueAsFloat(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getValueAsFloat();
	}
	return 0;
    }

    public long getPropertyTimeLeft(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe.getTimeLeft();
	}
	return 0;
    }

    public void setPropertyTimeLeft(String propertyName, long newTime)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		pe.setTime(newTime);
	}
    }

    public PropertyEntry getPropertyEntry(String propertyName)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
		return pe;
	}
	return null;
    }

    public void setProperty(String propertyName, String propertyValue)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
	    {
		pe.setValue(propertyValue);
		return;
	    }
	}
	// property not found, add to list
	properties.add(new PropertyEntry(propertyName, propertyValue, 0));
    }

    // notese que si se pasa como segundo parametro un String no se llamara
    // esta funcion, sino setProperty ( String , String ) por ser mas
    // especifica.
    public void setProperty(String propertyName, Object propertyValue)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
	    {
		pe.setValue(propertyValue.toString());
		pe.setObjectValue(propertyValue);
		return;
	    }
	}
	PropertyEntry pe = new PropertyEntry(propertyName, propertyValue
		.toString(), 0);
	pe.setObjectValue(propertyValue);
	properties.add(pe);
    }

    public void setProperty(String propertyName, int propertyValue)
    {
	setProperty(propertyName, String.valueOf(propertyValue));
    }

    public void setProperty(String propertyName, double propertyValue)
    {
	setProperty(propertyName, String.valueOf(propertyValue));
    }

    public void setProperty(String propertyName, float propertyValue)
    {
	setProperty(propertyName, String.valueOf(propertyValue));
    }

    public void setProperty(String propertyName, boolean propertyValue)
    {
	setProperty(propertyName, String.valueOf(propertyValue));
    }

    public void setProperty(String propertyName, String propertyValue,
	    long timeLeft)
    {
	for (PropertyEntry pe : properties)
	{
	    if (pe.getName().equalsIgnoreCase(propertyName))
	    {
		pe.setValue(propertyValue, timeLeft);
		return;
	    }
	}
	// property not found, add to list
	properties
		.add(new PropertyEntry(propertyName, propertyValue, timeLeft));
    }

    public void setProperty(String propertyName, int propertyValue,
	    long timeLeft)
    {
	setProperty(propertyName, String.valueOf(propertyValue), timeLeft);
    }

    public void setProperty(String propertyName, boolean propertyValue,
	    long timeLeft)
    {
	setProperty(propertyName, String.valueOf(propertyValue), timeLeft);
    }

    public boolean hasBooleanProperty(String propertyName)
    {
	return getPropertyValueAsBoolean(propertyName);
    }

}
